Back to Projects

Enemy AI System

Game - Revolunar (2026)

Unity C# Coroutines Navmesh Navigation

Implemented all enemy AI across 5+ enemy types, working in close collaboration with the enemy designer to ensure each behavior matched the intended gameplay feel. All enemy logic was built using coroutines paired with Unity's NavMesh system to produce fluid, reactive movement and combat.

Throughout development, I maintained regular feedback cycles with the project sponsor, Iron Galaxy, receiving guidance on code architecture, designer tooling, and debugging strategies that shaped the overall quality of the system.

Enemy Behaviors

Each enemy was built on a shared AIBehavior base class, with individual types overriding and extending behavior through coroutine-driven logic. This structure kept shared systems — detection, damage, freezing — centralized, while allowing each enemy to define its own movement patterns, attack routines, and range thresholds independently.

Detection & Line of Sight

Enemies use distance checks and raycasts against player and environment layer masks to determine when to engage. Rather than polling continuously, detection feeds directly into coroutine entry points — once a threshold is crossed, the appropriate behavior routine is launched and locked until it completes. This prevents state conflicts and keeps each enemy's decision loop clean and predictable.

Patrol & Idle Roaming

Enemies in a non-engaged state use NavMesh-driven patrol routes to move through the environment. Waypoint selection and dwell timers are exposed as serialized fields, allowing the designer to tune patrol feel per enemy type without touching code.

Ranged vs. Melee Logic

Each enemy has configurable range thresholds that determine which attack mode activates. A boolean rangeFlip field — added directly in response to designer feedback — inverts the range priority, letting the same enemy script support both "aggressive at close range" and "aggressive at distance" archetypes without duplicating logic.

Attack Patterns

Attack routines are implemented as coroutines, each managing their own timing, cooldowns, and cleanup. An isActing flag prevents multiple routines from overlapping mid-execution, keeping the enemy in a well-defined state at all times.

Projectile Volley

The volley attack fires a burst of projectiles in sequence, each with randomized horizontal and vertical spread applied via quaternion rotation around the direction-to-player vector. Shot count, spread angle, speed, damage, and delay between shots are all serialized. The designer can tune the feel of every enemy's volley independently in the inspector.

Laser Barrage

The laser uses a raycast that sweeps toward the player using Vector3.RotateTowards, with rotation speed lerped from a slow minimum to a fast maximum over the attack's duration. This creates a deliberately telegraphed attack — the laser starts wide and accelerates, rewarding players who move early and punishing hesitation. Damage is applied continuously as damagePerSecond × deltaTime whenever the ray hits the player layer.

Collaboration with Iron Galaxy

Iron Galaxy provided feedback across several areas of the system. On architecture, they encouraged keeping shared behavior in the base class and avoiding redundant per-enemy logic. On tooling, their input directly led to changes like the rangeFlip toggle and consistent use of [Header] attributes to keep the inspector readable for the designer. They also helped debug edge cases around coroutine timing and NavMesh agent conflicts on hover-type enemies.

Designer Collaboration

Throughout the project I worked closely with the enemy designer, translating intended behaviors into working implementations and iterating based on playtesting feedback. When a behavior didn't feel right in motion — an enemy that was too reactive, or an attack window that felt unfair — I'd discuss the feel with the designer and adjust exposed parameters or rework the underlying timing logic accordingly.

Inspector-Exposed Tooling

Every meaningful value in the enemy AI is serialized and organized with [Header] and [SerializeField] attributes. Damage values, cooldowns, range thresholds, spread angles, attack durations, and movement speeds can all be adjusted directly in the Unity inspector. The designer never needed to open a script to tune how an enemy felt — that was intentional from the start.

Results and Impact

* Implemented 5+ distinct enemy types, each with unique movement patterns, attack routines, and range behaviors, all built on a shared base class that kept the codebase maintainable as scope grew.

* The coroutine-based state model eliminated conflicting behavior states and made each enemy's logic easy to read, extend, and debug — a structure Iron Galaxy specifically noted as well-organized during reviews.

* Close collaboration with the enemy designer meant behaviors matched design intent from the start, reducing costly rework late in development. The rangeFlip toggle and fully exposed inspector parameters let the designer iterate independently without engineering bottlenecks.

* Feedback from Iron Galaxy on architecture, tooling, and debugging sharpened the overall quality of the system and directly influenced how shared logic was structured in the base class.

* The laser barrage's accelerating sweep mechanic became one of the more memorable enemy encounters in the game — a design outcome made possible by the tight feedback loop between implementation, playtesting, and iteration.